package com.homedo.odin.solution.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.homedo.core.common.base.dto.Meta;
import com.homedo.core.common.base.service.BaseService;
import com.homedo.core.common.exception.BizException;
import com.homedo.core.common.util.BeanUtils;
import com.homedo.core.common.util.DateUtils;
import com.homedo.core.common.util.PatternUtils;
import com.homedo.odin.solution.dao.AdPositionMapper;
import com.homedo.odin.solution.dao.AdResourceMapper;
import com.homedo.odin.solution.dao.AdServingMapper;
import com.homedo.odin.solution.dao.AdServingUserGroupMapper;
import com.homedo.odin.solution.dao.UserScoreMapper;
import com.homedo.odin.solution.enumcode.advertising.AdvertisingTypeEnum;
import com.homedo.odin.solution.enumcode.advertising.CodeTypeEnum;
import com.homedo.odin.solution.enumcode.advertising.PageEnum;
import com.homedo.odin.solution.enumcode.advertising.PositionTypeEnum;
import com.homedo.odin.solution.enumcode.basic.DeleteEnum;
import com.homedo.odin.solution.enumcode.basic.ShelfEnum;
import com.homedo.odin.solution.enumcode.basic.UserLevelEnum;
import com.homedo.odin.solution.enumcode.statistics.UserActionBusinessTypeEnum;
import com.homedo.odin.solution.model.entity.AdPosition;
import com.homedo.odin.solution.model.entity.AdResource;
import com.homedo.odin.solution.model.entity.AdServing;
import com.homedo.odin.solution.model.entity.AdServingUserGroup;
import com.homedo.odin.solution.model.request.mng.adservings.MngAdServingsListRequestBean;
import com.homedo.odin.solution.model.request.mng.adservings.MngAdServingsRequestBean;
import com.homedo.odin.solution.model.response.mng.adservings.MngAdServingsPageResponseBean;
import com.homedo.odin.solution.model.response.mng.adservings.MngAdServingsResponseBean;
import com.homedo.odin.solution.model.response.pc.AdServingsResponseBean;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p> 广告投放  服务实现类 </p>
 *
 * @author ZhangLei
 * @date 2018-05-15
 * @Description:
 */
@Service
public class AdServingServiceImpl extends BaseService<AdServingMapper, AdServing> {

    @Autowired
    private AdPositionMapper adPositionMapper;
    @Autowired
    private AdResourceMapper adResourceMapper;
    @Autowired
    private AdServingUserGroupMapper adServingUserGroupMapper;

    /**
     * MNG_发布新广告
     */
    @Transactional(rollbackFor = Exception.class)
    public void addAdvertising(MngAdServingsRequestBean requestBean) {
        String positionCode = requestBean.getPositionCode();
        AdPosition adPosition = adPositionMapper.selectByPositionCode(positionCode);
        if (adPosition == null) {
            throw new BizException(Meta.CODE_451.getCode(), "不存在该广告位 positionCode:" + positionCode);
        }

        //广告投放用户组
        List<AdServingUserGroup> userGroupList = null;
        //投放广告
        if (AdvertisingTypeEnum.PUT.getDbValue().equals(requestBean.getAdType())) {
            //必有上架时间  校验投放时间YYYY-MM-DD
            if (!PatternUtils.chekDay(requestBean.getShelveTime())) {
                throw new BizException(Meta.CODE_451.getCode(), "上架时间不合法 校验:yyyy-mm-dd");
            }
            //我的财富广告业务处理
            userGroupList = treasureAdvertising(requestBean, positionCode);
        } else {
            requestBean.setShelveTime(null);
            requestBean.setShelveDays(null);
            requestBean.setStatus(ShelfEnum.SHELF.getDbValue());

            //默认广告业务处理
            defaultAdvertising(positionCode,requestBean.getOperator());
        }

        //校验文本 图片 广告所需的必要参数是否存在
        checkTextOrResourceUrl(requestBean);

        AdResource adResource = insertResource(requestBean);
        AdServing adServing = insertServing(requestBean, adResource);

        //财富广告新增投放人群
        if (CollectionUtils.isNotEmpty(userGroupList)) {
            userGroupList.forEach(group -> {
                group.setAdServingId(adServing.getId());
            });
            adServingUserGroupMapper.insertList(userGroupList);
        }
    }

    /**
     * MNG_获取广告列表
     */
    public MngAdServingsPageResponseBean getAdvertising(MngAdServingsListRequestBean requestBean) {
        String shelveTimeStart = requestBean.getShelveTimeStart();
        String shelveTimeEnd = requestBean.getShelveTimeEnd();

        Date startDate = null;
        Date endDate = null;
        if (StringUtils.isNotBlank(shelveTimeStart) && StringUtils.isNotBlank(shelveTimeEnd)) {
            startDate = DateUtils.parseDate(shelveTimeStart, "yyyy-MM-dd");
            endDate = DateUtils.parseDate(shelveTimeEnd, "yyyy-MM-dd");
            DateUtils.compareLongTime(startDate, endDate);
        }
        PageHelper.startPage(requestBean.getPageNum(), requestBean.getPageSize());
        Date date = new Date();
        Date nowDate = DateUtils.getDate(date, "yyyy-MM-dd");
        Date beforeDate = DateUtils.getDate(DateUtils.beforeDate(date, 3), "yyyy-MM-dd");
        PageInfo<MngAdServingsResponseBean> pageInfo = new PageInfo<>(baseMapper
                .selectAdvertisingListByShelveTimeOrderByTime(startDate, endDate,
                        UserActionBusinessTypeEnum.ADVERTISING_SHOW.getDbValue(),
                        UserActionBusinessTypeEnum.ADVERTISING_CLICK.getDbValue(), beforeDate, nowDate));
        List<MngAdServingsResponseBean> dbList = pageInfo.getList();
        if (CollectionUtils.isEmpty(dbList)) {
            return null;
        }

        //转换用户等级显示   1234567 转换 全部 银牌 铜牌 钻石
        dbList.forEach(bean -> {
            String userGroupIds = bean.getUserGroupIds();
            if (StringUtils.isNotBlank(userGroupIds)) {
                String[] split = userGroupIds.split(",");
                if (split != null && split.length > 0) {
                    String userLevle = Arrays.stream(split)
                            .map(id -> UserLevelEnum.getExistDisplayValue(Integer.parseInt(id)))
                            .collect(Collectors.joining(","));
                    bean.setUserGroupIds(userLevle);
                }
            }
        });

        return BeanUtils.transfrom(MngAdServingsPageResponseBean.class, pageInfo);
    }

    /**
     * MNG_广告上下架
     */
    public void shelfStatus(Long userId, String adNumber, Integer status) {
        AdServing serving = new AdServing();
        serving.setAdNumber(adNumber);
        AdServing adServing = baseMapper.selectOne(serving);
        if (adServing == null) {
            throw new BizException(Meta.CODE_451.getCode(), "广告投放数据不存在 编号：" + adNumber);
        }
        adServing.setModifiedBy(userId);
        adServing.setStatus(status);

        if (ShelfEnum.SHELF.getDbValue().equals(status)) {
            Date date = new Date();
            adServing.setModifiedTime(date);
            adServing.setShelveTime(date);
        }
        baseMapper.updateById(adServing);
    }


    /**
     * PC_获取广告 根据 页面代码 区域代码 广告位代码 获取对应的所有广告
     */
    public List<AdServingsResponseBean> getAdServings(String code, String codeType) {
        List<AdServingsResponseBean> result = null;
        //获取页面所有广告
        if (CodeTypeEnum.PAGE.getDbValue().equals(codeType)) {
            result = getAdServingExecuteByCode(code, pageCodeAdServingExecute());
        }

        //获取地区所有广告
        if (CodeTypeEnum.AREA.getDbValue().equals(codeType)) {
            result = getAdServingExecuteByCode(code, areaCodeAdServingExecute());
        }

        //获取指定广告位广告
        if (CodeTypeEnum.POSITION.getDbValue().equals(codeType)) {
            result = getAdServingExecuteByCode(code, positionCodeAdServingExecute());
        }

        updateUserGroup(result);
        return result;
    }

    /**
     * 根据code代码获取指定用户等级广告
     */
    public List<AdServingsResponseBean> getAdServingsByUserLevel(String code, String codeType, Integer userLevel) {
        List<AdServingsResponseBean> result = null;

        //指定广告位
        if (CodeTypeEnum.POSITION.getDbValue().equals(codeType)) {
            //我的财富广告位中的广告需要去获取展示的用户等级
            if (code.contains(PageEnum.MY_TREASURE.getDbValue())) {
                result = getNewestTimeData(getAdServingExecuteByCodeAndUserId(code, userLevel, myTreasureAdServingExecute()));
            }
        }

        updateUserGroup(result);
        return result;
    }

    /**
     * MNG_所有广告位
     */
    public List<AdPosition> selectAllPositions() {
        List<AdPosition> list = adPositionMapper.selectAllPositions(DeleteEnum.NOT_DEL.getDbValue());
        return list;
    }


    /**
     * 广告Code码获取业务抽象
     */
    private List<AdServingsResponseBean> getAdServingExecuteByCode(String positionCode,
            Function<String, List<AdServingsResponseBean>> function) {
        return function.apply(positionCode);
    }

    /**
     * pageCode获取广告
     */
    private Function<String, List<AdServingsResponseBean>> pageCodeAdServingExecute() {
        return (code) -> {
            List<AdPosition> polist = adPositionMapper.selectByPageOrAreaOrPositionCode(code, null, null);
            return getAdServingsResponseBeans(polist);
        };
    }

    /**
     * areaCode获取广告
     */
    private Function<String, List<AdServingsResponseBean>> areaCodeAdServingExecute() {
        return (code) -> {
            List<AdPosition> polist = adPositionMapper.selectByPageOrAreaOrPositionCode(null, code, null);
            return getAdServingsResponseBeans(polist);
        };
    }

    /**
     * positionCode获取广告
     */
    private Function<String, List<AdServingsResponseBean>> positionCodeAdServingExecute() {
        return (code) -> {
            List<AdPosition> polist = adPositionMapper.selectByPageOrAreaOrPositionCode(null, null, code);
            return getAdServingsResponseBeans(polist);
        };
    }

    /**
     * 根据广告位获取广告
     */
    private List<AdServingsResponseBean> getAdServingsResponseBeans(List<AdPosition> polist) {
        if (CollectionUtils.isEmpty(polist)) {
            return null;
        }
        Set<String> positionCodeList = polist.stream()
                .filter(position -> position != null && StringUtils.isNotBlank(position.getPositionCode()))
                .map(position -> position.getPositionCode()).collect(Collectors.toSet());

        List<AdServingsResponseBean> dbList = baseMapper.selectByPositionCode(positionCodeList);

        List<AdServingsResponseBean> result = getNewestTimeData(dbList);
        return result;
    }

    private List<AdServingsResponseBean> getNewestTimeData(List<AdServingsResponseBean> dbList) {
        List<AdServingsResponseBean> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(dbList)) {
            Map<String, List<AdServingsResponseBean>> groupMap = dbList.stream()
                    .collect(Collectors.groupingBy(bean -> bean.getPositionCode()));
            groupMap.forEach((key, value) -> {
                if (CollectionUtils.isNotEmpty(value)) {
                    AdServingsResponseBean responseBean = getSortUpdateTimeLimitAdServing(value,
                            AdvertisingTypeEnum.PUT.getDbValue());
                    if (responseBean != null) {
                        //同一广告位取上架时间最新的一条记录
                        result.add(responseBean);
                    } else {
                        //取默认时间排序第一条
                        result.add(getSortUpdateTimeLimitAdServing(value, AdvertisingTypeEnum.DEFAULT.getDbValue()));
                    }
                }
            });
        }
        return result;
    }

    private AdServingsResponseBean getSortUpdateTimeLimitAdServing(List<AdServingsResponseBean> value, Integer adType) {
        return value.stream()
                .filter(bean -> bean != null && adType.equals(bean.getAdType()))
                .sorted((b1, b2) -> Long.compare(b2.getModifiedTime().getTime(), b1.getModifiedTime().getTime()))
                .limit(1).findFirst().orElse(null);
    }

    /**
     * 用户等级获取广告抽象
     */
    private List<AdServingsResponseBean> getAdServingExecuteByCodeAndUserId(String positionCode, Integer userId,
            BiFunction<String, Integer, List<AdServingsResponseBean>> function) {
        return function.apply(positionCode, userId);
    }

    /**
     * 根据用户等级获取我的财富广告位
     */
    private BiFunction<String, Integer, List<AdServingsResponseBean>> myTreasureAdServingExecute() {
        return (code, userLevel) -> {
            List<AdServingsResponseBean> responseBeans = baseMapper.selectByPositionCodeAndUserLevel(code, userLevel);
            if(CollectionUtils.isEmpty(responseBeans)){
                Set<String> set = new HashSet<>();
                set.add(code);
                responseBeans = baseMapper.selectByPositionCode(set);
            }
            return responseBeans;
        };
    }

    /**
     * 用户等级 1 2 3 转换为对应的 新人，铜牌等
     */
    private void updateUserGroup(List<AdServingsResponseBean> result) {
        if (CollectionUtils.isNotEmpty(result)) {
            //转换用户组
            result.forEach(bean -> {
                if (bean.getUserGroupId() != null) {
                    String displayUserGroup = UserLevelEnum.getExistDisplayValue(bean.getUserGroupId());
                    bean.setUserGroup(displayUserGroup);
                }
            });
        }
    }

    //校验文本 图片 广告所需的必要参数是否存在
    private void checkTextOrResourceUrl(MngAdServingsRequestBean requestBean) {
        boolean picFlag = PositionTypeEnum.PIC.getDbValue().equals(requestBean.getResourceType());
        boolean videoFlag = PositionTypeEnum.VIDEO.getDbValue().equals(requestBean.getResourceType());
        if (picFlag || videoFlag) {
            if (StringUtils.isBlank(requestBean.getResourceUrl())) {
                throw new BizException(Meta.CODE_451.getCode(), "视频或图片的链接资源不能为空");
            }
            requestBean.setText(null);
        }

        boolean textFlag = PositionTypeEnum.TEXT.getDbValue().equals(requestBean.getResourceType());
        if (textFlag) {
            if (StringUtils.isBlank(requestBean.getText())) {
                throw new BizException(Meta.CODE_451.getCode(), "广告文本资源不能为空");
            }
            requestBean.setResourceUrl(null);
        }
    }

    //我的财富广告业务处理
    private List<AdServingUserGroup> treasureAdvertising(MngAdServingsRequestBean requestBean, String positionCode) {
        List<AdServingUserGroup> list = null;

        //财富广告 才允许有投放人群(my_treasure)
        if (positionCode.contains(PageEnum.MY_TREASURE.getDbValue())) {
            //新增用户组与广告投放绑定
            List<Integer> userGroupIds = requestBean.getUserGroupIds();
            if (CollectionUtils.isEmpty(userGroupIds)) {
                throw new BizException(Meta.CODE_451.getCode(), "投放用户组没有参数");
            }
            list = userGroupIds.stream()
                    .filter(userGroupId -> userGroupId != null && UserLevelEnum.dbValueExist(userGroupId))
                    .map(userGroupId -> {
                        AdServingUserGroup group = new AdServingUserGroup();
                        group.setUserGroupId(userGroupId);
                        return group;
                    }).collect(Collectors.toList());
        }
        return list;
    }

    //默认广告业务处理
    private void defaultAdvertising(String positionCode,String operator) {
        //同一广告位，有默认广告，新默认替代老默认
        List<AdServing> dbList = baseMapper
                .selectByPositionCodeAndAdType(positionCode, AdvertisingTypeEnum.DEFAULT.getDbValue());

        if (CollectionUtils.isNotEmpty(dbList)) {
            dbList.forEach(bean->{
                Date date = new Date();
                //老默认删除  默认广告只可能存在一条数据，循环删除即可
                bean.setIsDel(DeleteEnum.IS_DEL.getDbValue());
                bean.setModifiedTime(date);
                bean.setOperator(operator);
                baseMapper.updateById(bean);

                //投放对应的素材也删除
                AdResource dbResource = adResourceMapper.selectById(bean.getResourceId());
                if (dbResource != null) {
                    dbResource.setIsDel(DeleteEnum.IS_DEL.getDbValue());
                    dbResource.setModifiedTime(date);
                    adResourceMapper.updateById(dbResource);
                }
            });
        }
    }


    private AdServing insertServing(MngAdServingsRequestBean requestBean, AdResource adResource) {
        AdServing adServing = BeanUtils.transfrom(AdServing.class, requestBean);
        String adServingNumber = getAdServingNumber();
        adServing.setAdNumber(adServingNumber);
        adServing.setCreatedBy(requestBean.getUserId());
        adServing.setModifiedBy(requestBean.getUserId());
        adServing.setResourceId(adResource.getId());

        if (StringUtils.isNotBlank(requestBean.getShelveTime())) {
            adServing.setShelveTime(DateUtils.parseDate(requestBean.getShelveTime(), "yyyy-MM-dd"));
        }
        baseMapper.insert(adServing);
        return adServing;
    }


    /**
     * 获取  fagg20180611001  AdServingNumber递增
     * @return
     */
    //广告前缀
    private final String ADSERING = "fagg";
    private final String DEFAULT_NUMBER = "001";
    private final char ZERO = '0';
    private String getAdServingNumber() {
        AdServing dbBean = baseMapper.selectByCreateTimeLimitOne();
        String dateStr = LocalDate.now().toString().replace("-", "");
        StringBuilder sb = new StringBuilder();
        if (dbBean != null) {
            String adNumber = dbBean.getAdNumber();
            if (StringUtils.isBlank(adNumber)) {
                throw new BizException(Meta.CODE_451.getCode(), "广告编号为空");
            }
            String number = adNumber.substring(12);
            StringBuilder sb1 = new StringBuilder();
            for (int i = 0; i < number.length(); i++) {
                char c = number.charAt(i);
                if(i!=number.length()-1&&Character.compare(ZERO,number.charAt(i))==0){
                    sb1.append(number.charAt(i));
                    continue;
                }else{
                    int newNum = Integer.parseInt(number) + 1;
                    sb1.append(newNum);
                    sb.append(ADSERING).append(dateStr).append(sb1.toString());
                    break;
                }
            }
        } else {
            sb.append(ADSERING).append(dateStr).append(DEFAULT_NUMBER);
        }
        return sb.toString();
    }

    private AdResource insertResource(MngAdServingsRequestBean requestBean) {
        AdResource adResource = BeanUtils.transfrom(AdResource.class, requestBean);
        adResource.setCreatedBy(requestBean.getUserId());
        adResource.setModifiedBy(requestBean.getUserId());
        adResourceMapper.insert(adResource);
        return adResource;
    }

    /**
     * 定时投放未上架广告
     */
    public void timingToAdvertise() {
        baseMapper.timingToAdvertise();
    }

    /**
     * 定时投放未上架广告
     */
    public void shelvesAdvertise() {
        baseMapper.shelvesAdvertise();
    }
}
